package com.tianji.learning.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.service.ILearningRecordService;
import io.swagger.models.auth.In;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.beans.Beans;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author author
 * @since 2023-11-06
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper recordMapper;
    private final PointsRecordMapper pointsRecordMapper;

    /**
     * 添加课程到课表
     * @param userId
     * @param courseIds
     */
    @Override
    public void addUserLessons(Long userId, List<Long> courseIds) {
        //1.通过feign调用远程调用课程服务，获取课程信息
        List<CourseSimpleInfoDTO> cinfos = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(cinfos)){
            log.error("课程信息不存在，无法添加到课表");
            return;
        }
        //2.封装 learningLesson po实体类，填充过期时间
        List<LearningLesson> list = new ArrayList<>();
        for (CourseSimpleInfoDTO cinfo : cinfos) {
            LearningLesson lesson = new LearningLesson();
            // 2.1.填充 userId 和 courseId
            lesson.setUserId(userId);
            lesson.setCourseId(cinfo.getId());
            //2.2 获取课程有效期，单位为月
            Integer validDuration = cinfo.getValidDuration();
            if (validDuration != null) {
                LocalDateTime expireTime = LocalDateTime.now();
                lesson.setCreateTime(LocalDateTime.now());
                lesson.setExpireTime(expireTime.plusMonths(validDuration));
            }
            list.add(lesson);
        }
        //3.批量保存
        this.saveBatch(list);
    }

    /**
     * 分页查询我的课表
     * @param query
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        //1.获取当前登陆人
        Long userId = UserContext.getUser();
        //2.分页查询我的课表
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .page(query.toMpPage("latest_learn_time", false));

        List<LearningLesson> records = page.getRecords();
        if (CollUtils.isEmpty(records)){
            return PageDTO.empty(page);
        }
        //3.远程调用课程服务，给vo中的 课程名、课程封面、章节数 赋值
            //获取课程集ids，因为调用远程课程服务需要传入课程集ids
        List<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());
            //调用远程课程服务，获取课程信息
        List<CourseSimpleInfoDTO> cinfos = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(courseIds)){
            return PageDTO.empty(page);
        }

        //将cinfos 课程集合 转换为map结构 <id,课程对象>
        Map<Long, CourseSimpleInfoDTO> infoDTOMap = cinfos.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));

        //4.将po中的数据 封装到vo中
        List<LearningLessonVO> voList = new ArrayList<>();
        for (LearningLesson record : records) {
            //LearningLessonVO vo = new LearningLessonVO();
            //BeanUtils.copyProperties(record, vo);
            LearningLessonVO vo = BeanUtils.copyBean(record, LearningLessonVO.class);

            CourseSimpleInfoDTO infoDTO = infoDTOMap.get(record.getCourseId()); //从课程集合对象中利用id（key）获取课程对象
            if (infoDTO!= null) {
                vo.setCourseName(infoDTO.getName());
                vo.setCourseCoverUrl(infoDTO.getCoverUrl());
                vo.setSections(infoDTO.getSectionNum());
            }
            voList.add(vo);
        }
        //5.返回
        return PageDTO.of(page, voList);
    }

    /**
     * 查询我的当前课程
     * @return
     */
    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        //1.获取当前用户Id
        Long userId = UserContext.getUser();
        //2.查询用户最近的课程 按latest_learn_time排序 取第一条  正在学习中：status=1
        //select * from xx where user_id = #{userId} and status = 1 order by latest_learn_time desc limit 1
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING)
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        if (lesson == null){
            return null;
        }
        //3.远程调用课程服务，给vo添加课程名、课程封面、章节数
        CourseFullInfoDTO cinfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (cinfo == null){
            throw new BizIllegalException("课程不存在");
        }
        //4.查询当前用户课表，总的课程数
        //select count(*) from learning_lesson where user_id = #{userId}
        Integer count = this.lambdaQuery().eq(LearningLesson::getUserId, userId).count();

        //5.通过feign调用远程服务  获取小节名称 和 小节编号
        List<CataSimpleInfoDTO> cataInfos =
                catalogueClient.batchQueryCatalogue(CollUtils.singletonList(lesson.getLatestSectionId()));

        if (CollUtils.isEmpty(cataInfos)){
            throw new BizIllegalException("小节不存在");
        };
        //6.封装给vo，返回
        LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        vo.setCourseName(cinfo.getName());
        vo.setCourseCoverUrl(cinfo.getCoverUrl());   //课程封面
        vo.setSections(cinfo.getSectionNum());       //章节数
        vo.setCourseAmount(count);                   //课程总数
        CataSimpleInfoDTO cataSimpleInfoDTO = cataInfos.get(0);
        vo.setLatestSectionName(cataSimpleInfoDTO.getName());    //最近学习的小节名
        vo.setLatestSectionIndex(cataSimpleInfoDTO.getCIndex()); //最近学习的小节编号

        return vo;
    }

    /**
     * 判断用户是否可以继续学习当前课程
     * @param courseId
     * @return
     */
    @Override
    public Long isLessonValid(Long courseId) {
        //1.获取当前登录用户id
        Long userId = UserContext.getUser();
        //2.查询我的课表learning_lesson 条件：user_id = #{userId} and course_id = #{courseId} and status = 1
            //数据库表中，userId和 courseId，有一个联合索引，所以最后直接 .one
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();

        if (lesson == null){
            return null;
        }
        //3.判断课程是否过期
        LocalDateTime expireTime = lesson.getExpireTime();
        LocalDateTime now = LocalDateTime.now();
        if (expireTime != null && now.isAfter(expireTime)){
            return null;
        }
        return lesson.getId();
    }

    /**
     * 查询用户课表中指定的课程状态
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO queryLessonByCourseId(Long courseId) {
        //1.获取当前登录用户id
        Long userId = UserContext.getUser();
        //2.查询我的课表learning_lesson 条件：user_id = #{userId} and course_id = #{courseId} and status = 1
        //数据库表中，userId和 courseId，有一个联合索引，所以最后直接 .one
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();

        if (lesson == null){
            return null;
        }
        //3.po转vo
        LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        return vo;
    }

    /**
     * 创建学习计划
     * @param courseId
     * @param freq
     */
    @Override
    public void createLearningPlan(Long courseId, Integer freq) {
        //1.获取当前登录用户id
        Long userId = UserContext.getUser();
        //2.查询课表中指定课程有关的数据
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if (lesson == null){
            throw new BizIllegalException("课程不存在");
        }
        //3.修改数据
        this.lambdaUpdate()
                .set(LearningLesson::getWeekFreq,freq)
                .set(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .eq(LearningLesson::getId,lesson.getId())
                .update();
    }

    /**
     * 查询我的学习计划
     * @param query
     * @return
     */
    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery query) {
        //1.获取当前登录用户id
        Long userId = UserContext.getUser();

        //3.查询本周学习计划总数据，learning_plan 条件：userId status in (1,2) plan_status = 1 sum（week_freq）

        //select sum(week_freq) from learning_plan where user_id = #{userId} and status in (1,2))
        QueryWrapper<LearningLesson> wrapper = new QueryWrapper<>();
        wrapper.select("sum(week_freq) as planTotal"); //查询哪些列
        wrapper.eq("user_id",userId);
        wrapper.in("status",LessonStatus.NOT_BEGIN,LessonStatus.LEARNING);
        wrapper.eq("plan_status",PlanStatus.PLAN_RUNNING);
        Map<String, Object> map = this.getMap(wrapper);
        Integer planTotal = 0;     //预防空指针异常，sum(week_freq)可能为null
        if (map != null && map.get("planTotal") != null) {
            planTotal = Integer.valueOf(map.get("planTotal").toString());
        }

        //4.查询本周，实际已学习的小节总数据 learning_record userId finished=1 finished_time在本周 count(*)
        LocalDate now = LocalDate.now();
        LocalDateTime weekBeginTime = DateUtils.getWeekBeginTime(now); //本周开始时间
        LocalDateTime weekEndTime = DateUtils.getWeekEndTime(now);     //本周结束时间
        Integer weekFinishedPlanNum = recordMapper.selectCount(Wrappers.<LearningRecord>lambdaQuery()
                .eq(LearningRecord::getUserId, userId)
                .eq(LearningRecord::getFinished, true)
                .between(LearningRecord::getFinishTime, weekBeginTime, weekEndTime));

        //TODO 2.查询本周学习积分
        Integer weekPoints = 0;
        weekPoints = pointsRecordMapper.queryPointsByUserId(userId, weekBeginTime, weekEndTime);

        //5.查询课表数据 learning_lesson userId status in (0,1) plan_status = 1 分页
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        if (BeanUtils.isEmpty(records)){
            LearningPlanPageVO vo = new LearningPlanPageVO();
            vo.setTotal(0L);
            vo.setPages(0L);
            vo.setList(CollUtils.emptyList());
            return vo;
        }

        //6.远程调用课程服务。获取课程信息
        List<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());//课程ids
        List<CourseSimpleInfoDTO> cinfos = courseClient.getSimpleInfoList(courseIds); //课程信息
        if (BeanUtils.isEmpty(cinfos)){
            throw new BizIllegalException("课程不存在！");
        }

        //将 cinfos list 结构 转map <课程Id,CourseSimpleInfoDTO>
        Map<Long, CourseSimpleInfoDTO> cinfoMap = cinfos.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));

        //7.查询学习记录learning_record 本周 当前用户下 每一门课下 已学习的小节数量
        QueryWrapper<LearningRecord> rWrapper = new QueryWrapper<>();
        rWrapper.select("lesson_id as lessonId","count(*) as userId");
        rWrapper.eq("user_id",userId);;
        rWrapper.eq("finished",true);
        rWrapper.between("finish_time", weekBeginTime, weekEndTime);
        rWrapper.groupBy("lesson_id");
        List<LearningRecord> learningRecords = recordMapper.selectList(rWrapper); //学习记录

        //map 中的key 是 learning_record 中的 lessonId value 是 当前用户对应课程下已学习的小节数量
        Map<Long, Long> courseWeekFinishNumMap = learningRecords.stream().collect(Collectors.toMap(LearningRecord::getLessonId, c -> c.getUserId()));

        //8.封装vo
        LearningPlanPageVO vo = new LearningPlanPageVO();
        vo.setWeekTotalPlan(planTotal);
        vo.setWeekFinished(weekFinishedPlanNum); //本周已学习的小节数
        vo.setWeekPoints(weekPoints);
        List<LearningPlanVO> voList = new ArrayList<>();
        for (LearningLesson record : records) {
            LearningPlanVO planVO = BeanUtils.copyBean(record, LearningPlanVO.class);
            CourseSimpleInfoDTO infoDTO = cinfoMap.get(record.getCourseId());
            if (infoDTO!= null){
                planVO.setCourseName(infoDTO.getName());  //课程名称
                planVO.setSections(infoDTO.getSectionNum()); //课程总节数
            }
            planVO.setWeekLearnedSections(courseWeekFinishNumMap.getOrDefault(record.getId(),0L).intValue()); //本周已学习的小节数

            voList.add(planVO);
        }

        return vo.pageInfo(page.getTotal(),page.getPages(),voList);
    }

    //退款，删除课程
    @Override
    public void deleteCourseFromLesson(Long userId, Long courseId) {
        // 1.获取当前登录用户
        if (userId == null) {
            userId = UserContext.getUser();
        }
        // 2.删除课程
        remove(buildUserIdAndCourseIdWrapper(userId, courseId));
    }

    //统计课程学习人数
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        // select count(1) from xx where course_id = #{cc} AND status in (0, 1, 2)
        return lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .in(LearningLesson::getStatus,
                        LessonStatus.NOT_BEGIN.getValue(),
                        LessonStatus.LEARNING.getValue(),
                        LessonStatus.FINISHED.getValue())
                .count();
    }

    private LambdaQueryWrapper<LearningLesson> buildUserIdAndCourseIdWrapper(Long userId, Long courseId) {
        LambdaQueryWrapper<LearningLesson> queryWrapper = new QueryWrapper<LearningLesson>()
                .lambda()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        return queryWrapper;
    }
}
